using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

using IGlobalist.Net.Rtp;
using IGlobalist.LectureTool.Controls;
using IGlobalist.LectureService;
namespace IGlobalist
{
    public partial class FrmMain : Form
    {
        private ChatTab cTab;

        private RtpSender rtpSenderVideo;
        private RtpSender rtpSenderChat;
        private RtpSender rtpSenderFile;
        private RtpSender rtpSenderDesktop;
        private RtpSender rtpSenderVoice;

        private CaptureVideo vCapture = null;
        private CaptureAudio aCapture = null;
        private CaptureDesktop dkCapture = null;

        private List<ShowVideo> svs = new System.Collections.Generic.List<ShowVideo>();
        private List<ShowAudio> sas = new System.Collections.Generic.List<ShowAudio>();

        private Dictionary<uint, RtpParticipant> partTable = new Dictionary<uint, RtpParticipant>();

        BinaryFormatter bf = new BinaryFormatter();

        private bool disposed = false;
        public FrmMain()
        {
            InitializeComponent();
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            FrmLogin frmLogin = new FrmLogin();
            if (frmLogin.ShowDialog() == DialogResult.OK)
            {
                Global.MainForm = this;
                if (cTab == null)
                {
                    this.SuspendLayout();
                    tcChats.Visible = false;
                    cTab = new ChatTab("Main chat room");                       
                    tcChats.TabPages.Add(cTab);
                    tcChats.Visible = true;
                    this.ResumeLayout();
                }
                FrmMediaConfig mConfig = new FrmMediaConfig();
                mConfig.ShowDialog();
                if (!Global.SourceDescription.IsMainSource)
                {
                    menuAdmin.Enabled = false;
                    mnuiSendDesktop.Enabled = false;
                    HookRtpEvents();
                    JoinRtpSession();
                    cTab.RtpSender = rtpSenderChat;
                    SessionSettings settings = Global.Svc.GetSessionSettings();
                    mnuiSendDesktop.Enabled = btnStartDesktop.Enabled = settings.MemberCanSendDesktop;
                    mnuiSendFile.Enabled = btnSendFile.Enabled = settings.MemberCanSendFile;
                    mnuiSendVoice.Enabled = btnStartVoice.Enabled = settings.MemberCanSendVoice && Global.HasAudioInput;
                    mnuiSendWC.Enabled = btnStartWC.Enabled = settings.MemberCanSendWebcam && Global.HasVideoInput;
                    Global.Settings = settings;
                }
                else
                {
                    FrmOptions options = new FrmOptions();
                    options.ShowDialog(this);
                    menuTool.Enabled = toolStripMain.Enabled = Global.ServiceStarted;
                    mnuiSendVoice.Enabled = btnStartVoice.Enabled = Global.HasAudioInput;
                    mnuiSendWC.Enabled = btnStartWC.Enabled = Global.HasVideoInput;

                }                
            }
            else
            {
                this.Close();
            }
        }
        
        private void mnuiStartCastService_Click(object sender, EventArgs e)
        {
            Application.DoEvents();
            //Do start service
            if ("Start service".Equals(mnuiStartCastService.Text))
            {
                //try stop first
                try
                {
                    Global.Svc.StopCastService();
                }
                catch { }
                if (Global.Svc.StartCastService())
                {
                    Global.CastServiceDescription = Global.Svc.GetCastServiceDesc();
                    lblStatus.Text = "Lecture cast service is started";
                    mnuiStartCastService.Text = "Stop service";
                    HookRtpEvents();
                    JoinRtpSession();
                    
                    cTab.RtpSender = rtpSenderChat;
                    disposed = false;
                    menuSystem.Enabled = menuTool.Enabled = toolStripMain.Enabled = Global.ServiceStarted = true;
                }
                else
                {
                    MessageBox.Show("Can not start service. Please try again", "Service error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
            else
            {
                if (Global.Svc.StopCastService())
                {
                    lblStatus.Text = "Lecture cast service is stoped";
                    mnuiStartCastService.Text = "Start service";
                    Cleanup();
                    menuTool.Enabled = toolStripMain.Enabled = Global.ServiceStarted = false;
                }
                else
                {
                    MessageBox.Show("Can not stop service. Please try again", "Service error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
        }


        private void HookRtpEvents()
        {
            RtpEvents.RtpParticipantAdded += new RtpEvents.RtpParticipantAddedEventHandler(RtpParticipantAdded);
            RtpEvents.RtpParticipantRemoved += new RtpEvents.RtpParticipantRemovedEventHandler(RtpParticipantRemoved);
            RtpEvents.RtpStreamAdded += new RtpEvents.RtpStreamAddedEventHandler(RtpStreamAdded);
            RtpEvents.RtpStreamRemoved += new RtpEvents.RtpStreamRemovedEventHandler(RtpStreamRemoved);
        }

        private IPEndPoint localEndPoint;
        private IPEndPoint castEndPoint;
        private void JoinRtpSession()
        {
            int port = 12000;
            int rPort = Convert.ToInt32(Global.CastServiceDescription.ServiceEndPoint.Split(':')[1]);
            //localEndPoint = new IPEndPoint(GetAddress(), port);
            localEndPoint = new IPEndPoint(Global.LocalIPAddress, port);
            castEndPoint = new IPEndPoint(IPAddress.Parse(Global.CastServiceDescription.ServiceEndPoint.Split(':')[0]), rPort);

            RtpParticipant part = new RtpParticipant(
                Global.SourceDescription.UserName, 
                Global.SourceDescription.UserName,
                Global.SourceDescription.IsMainSource, 
                true, 
                true, 
                localEndPoint);
            Global.RtpSession = new RtpSession(localEndPoint, castEndPoint, part, true, true, false);
            rtpSenderChat = Global.RtpSession.CreateRtpSenderFec(
                Global.SourceDescription.UserName, 
                PayloadType.Chat, null, 0, 200, castEndPoint);
            //some trick here
            rtpSenderChat.Send((BufferChunk) System.Text.Encoding.UTF8.GetBytes("..........................................................\n"));
            rtpSenderChat.Send((BufferChunk) System.Text.Encoding.UTF8.GetBytes("..........................................................\n"));
        }
        
        private void Cleanup()
        {
            if (!disposed)
            {
                UnhookRtpEvents();
                LeaveRtpSession();
                disposed = true;
            }      
            foreach(ShowAudio sa in sas){
                if (sa != null)
                {
                    sa.Dispose();
                }
            }
            foreach (ShowVideo sv in svs)
            {
                if (sv != null)
                {
                    sv.Dispose();
                }
            }
            if (vCapture != null)
            {
                vCapture.Dispose();
                vCapture = null;
            }
            if (aCapture != null)
            {
                aCapture.Dispose();
                aCapture = null;
            }
            if (dkCapture != null)
            {
                dkCapture.Dispose();
                dkCapture = null;
            }
        }

        private void UnhookRtpEvents()
        {
            RtpEvents.RtpParticipantAdded -= new RtpEvents.RtpParticipantAddedEventHandler(RtpParticipantAdded);
            RtpEvents.RtpParticipantRemoved -= new RtpEvents.RtpParticipantRemovedEventHandler(RtpParticipantRemoved);
            RtpEvents.RtpStreamAdded -= new RtpEvents.RtpStreamAddedEventHandler(RtpStreamAdded);
            RtpEvents.RtpStreamRemoved -= new RtpEvents.RtpStreamRemovedEventHandler(RtpStreamRemoved);
        }

        private void LeaveRtpSession()
        {
            if (Global.RtpSession != null)
            {
                Global.RtpSession.Dispose();
                Global.RtpSession = null;
            }
            if (rtpSenderVideo != null)
            {
                rtpSenderVideo.Dispose();
                rtpSenderVideo = null;
            }
            if (rtpSenderChat != null)
            {
                rtpSenderChat.Dispose();
                rtpSenderChat = null;
            }
            if (rtpSenderVoice != null)
            {
                rtpSenderVoice.Dispose();
                rtpSenderVoice = null;
            }

            if (rtpSenderDesktop != null)
            {
                rtpSenderDesktop.Dispose();
                rtpSenderDesktop = null;
            }
            if (rtpSenderFile != null)
            {
                rtpSenderFile.Dispose();
                rtpSenderFile = null;
            }
        }
        private void RtpParticipantAdded(object sender, RtpEvents.RtpParticipantEventArgs ea)
        {
            if (partTable.ContainsKey(ea.RtpParticipant.SSRC)) return;
            partTable.Add(ea.RtpParticipant.SSRC, ea.RtpParticipant);
            string epString = ea.RtpParticipant.GetPrivateExtension(Rtcp.PED_PARTICIPANT_EP);
            string msg = string.Format("{0} - ({1}) has joined the chat session.\n", ea.RtpParticipant.Name, epString);            
            CrossThreadOperation.Invoke(this, delegate { ShowMessage(msg); });
            CrossThreadOperation.Invoke(this, delegate { AddParticipant(ea.RtpParticipant); });
        }

        private void RtpParticipantRemoved(object sender, RtpEvents.RtpParticipantEventArgs ea)
        {
            string msg = string.Format("{0} has left the chat session.\n", ea.RtpParticipant.Name);
            CrossThreadOperation.Invoke(this, delegate { ShowMessage(msg); });
            CrossThreadOperation.Invoke(this, delegate { RemoveParticipant(ea.RtpParticipant); });
        }

        private void RtpStreamAdded(object sender, RtpEvents.RtpStreamEventArgs ea)
        {
            if (ea.RtpStream.PayloadType == PayloadType.dynamicPresentation)
            {
                RtpStream s = ea.RtpStream;
                if (s.Name.ToLower().Equals("connect-point")) return;
                s.IsUsingNextFrame = true;
            
                ShowVideo sv = CrossThreadOperation.Invoke<ShowVideo>(this, delegate { return CreateDesktopShow(s); });           
                try
                {                    
                    sv.Start();
                    svs.Add(sv);
                }
                catch
                {
                    sv.Dispose();
                }
            }
            if (ea.RtpStream.PayloadType == PayloadType.dynamicVideo)
            {
                RtpStream s = ea.RtpStream;
                if (s.Name.ToLower().Equals("connect-point")) return;

                s.IsUsingNextFrame = true;            
                ShowVideo sv = CrossThreadOperation.Invoke<ShowVideo>(this, delegate { return CreateShow(s); });
                try
                {
                    sv.Start(); 
                    svs.Add(sv);
                }
                catch
                {
                    sv.Dispose();                    
                }
            }
            if (ea.RtpStream.PayloadType == PayloadType.dynamicAudio)
            {
                RtpStream s = ea.RtpStream;
                if (s.Name.ToLower().Equals("connect-point")) return;

                s.IsUsingNextFrame = true;
          
                //ShowAudio sa = CrossThreadOperation.Invoke<ShowAudio>(this, delegate { return CreateShowA(s); });
                ShowAudio sa = new ShowAudio(s);
                try
                {
                    sa.Start();
                    sas.Add(sa);
                }
                catch
                {
                    sa.Dispose();                   
                }
            }
            if (ea.RtpStream.PayloadType == PayloadType.Chat)
            {
                ea.RtpStream.FrameReceived += new RtpStream.FrameReceivedEventHandler(FrameReceived);
            }
            if (ea.RtpStream.PayloadType == PayloadType.FileTransfer)
            {
                ea.RtpStream.FrameReceived += new RtpStream.FrameReceivedEventHandler(FileFrameReceived);
            }
        }
        private void RtpStreamRemoved(object sender, RtpEvents.RtpStreamEventArgs ea)
        {
            ea.RtpStream.FrameReceived -= new RtpStream.FrameReceivedEventHandler(FrameReceived);
        }
        private object getFileLock = new object();
        private void FileFrameReceived(object sender, RtpStream.FrameReceivedEventArgs ea)
        {
            lock (getFileLock)
            {
                MemoryStream ms = new MemoryStream((byte[])ea.Frame);
                try
                {
                    FileObject fileObj = (FileObject)bf.Deserialize(ms);
                    string fileName = Path.GetFileName(fileObj.Name);
                    string name = ea.RtpStream.Name;
                    if (Global.SourceDescription.Name.Equals(name)) return;
                    if (fileName.ToLower().Equals("testtransfer.test")) return;
                    string msg = "User: {0} has sent you a file - [{1}].\nDo you want to get it ?";
                    msg = string.Format(msg, name, fileName);
                    if (MessageBox.Show(msg, "File received", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                        == DialogResult.Yes)
                    {
                        dlgSendFile.FileName = fileName;
                        DialogResult result = CrossThreadOperation.Invoke<DialogResult>(this, delegate { return dlgSendFile.ShowDialog(this); });
                        if (result == DialogResult.OK)
                        {
                            fileName = dlgSendFile.FileName;
                            if (File.Exists(fileName))
                            {
                                if (MessageBox.Show(string.Format("File: {0} already exists. Overwrite it ?", fileName), "Read file error", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                                    == DialogResult.No)
                                    return;
                            }
                            FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
                            fs.Write(fileObj.Data, 0, fileObj.Data.Length);
                            fs.Close();
                        }
                    }
                }
                catch (Exception ex)
                {
                    IGlobalist.Net.Logger.RtpLogger.Error(ex);
                }
                finally
                {
                    ms.Dispose();
                    ms = null;
                }
            }
        }
        private void FrameReceived(object sender, RtpStream.FrameReceivedEventArgs ea)
        {
            string msg =  (string)ea.Frame;
            CrossThreadOperation.Invoke(this, delegate { ShowMessage(msg); });
        }

        private void RemoveParticipant(RtpParticipant p)
        {
            for (int i = 0; i < listUsers.Items.Count; i++)
            {
                ExListBoxItem listItem = (ExListBoxItem)listUsers.Items[i];
                if (listItem.Id == p.SSRC)
                {
                    listUsers.Items.RemoveAt(i);
                    return;
                }
            }
        }
        private void AddParticipant(RtpParticipant p)
        {
            string s = "Camera: {0}, Phone: {1}";
            ExListBoxItem listItem = new ExListBoxItem((int) p.SSRC, p.CName, 
                string.Format(s, p.HasVideo, p.HasAudio) , 
                imageListMenu.Images[0]);
            listUsers.Items.Add(listItem);            
        }
        private void ShowMessage(string msg)
        {            
            Font f = new Font(new FontFamily("verdana"),10.0f,  FontStyle.Bold | FontStyle.Italic);
            cTab.SetText(msg, f, RtfColor.Red);          
        }
        private ShowVideo CreateShow(RtpStream s)
        {
            VideoFrame vf = new VideoFrame();
            vf.UserName = s.Name;
            flPanelVideos.Controls.Add(vf);
            return new ShowVideo(vf.pnlVideo, s);            
        }
        private ShowVideo CreateDesktopShow(RtpStream s)
        {
            return new ShowVideo(pnlDesktop, s);
        }
        private ShowAudio CreateShowA(RtpStream s)
        {
            //return new ShowAudio(s);
            return null;
        }
        private IPAddress GetAddress()
        {
            IPAddress[] addrs = Dns.GetHostAddresses(Dns.GetHostName());
            foreach (IPAddress addr in addrs)
            {
                if (!IPAddress.IsLoopback(addr))
                    return addr;
            }
            return IPAddress.Loopback;
        }

        private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (MessageBox.Show("Are you sure you want to quit ?",
                    "Warning",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question)
                            == DialogResult.No)
            {
                e.Cancel = true;
                return;
            }
            try
            {
                if (Global.SourceDescription.IsMainSource)
                    Global.Svc.StopCastService();
                if (!disposed) Cleanup();
            }
            catch { }

        }

        private void mnuiSendWC_Click(object sender, EventArgs e)
        {
            Application.DoEvents();
            if ("Send webcam".Equals(mnuiSendWC.Text))
            {
                if (vCapture == null)
                {
                    rtpSenderVideo = Global.RtpSession.CreateRtpSenderFec(Global.SourceDescription.Name, PayloadType.dynamicVideo, null, 0, 200, castEndPoint);
                    vCapture = new CaptureVideo(rtpSenderVideo);
                }
                try
                {
                    vCapture.Start();
                    mnuiSendWC.Text = "Stop webcam";
                }
                catch { }
            }
            else
            {
                try
                {
                    vCapture.Pause();
                    mnuiSendWC.Text = "Send webcam";
                }
                catch { }
            }
            btnStartWC.Checked = mnuiSendWC.Text.Equals("Stop webcam");
        }

        private void mnuiSendDesktop_Click(object sender, EventArgs e)
        {
            Application.DoEvents();
            if ("Send desktop".Equals(mnuiSendDesktop.Text))
            {
                if (dkCapture == null)
                {
                    rtpSenderDesktop = Global.RtpSession.CreateRtpSenderFec(Global.SourceDescription.Name, PayloadType.dynamicPresentation, null, 0, 200, castEndPoint);
                    dkCapture = new CaptureDesktop(rtpSenderDesktop);
                }
                try
                {
                    dkCapture.Start();
                    mnuiSendDesktop.Text = "Stop desktop";
                }
                catch { }
            }
            else
            {
                try
                {
                    dkCapture.Pause();
                    mnuiSendDesktop.Text = "Send desktop";
                }
                catch { }
            }
            btnStartDesktop.Checked = mnuiSendDesktop.Text.Equals("Stop desktop");
        }

        private void mnuiSendVoice_Click(object sender, EventArgs e)
        {
            Application.DoEvents();
            if ("Send voice".Equals(mnuiSendVoice.Text))
            {
                if (aCapture == null)
                {
                    rtpSenderVoice = Global.RtpSession.CreateRtpSenderFec(Global.SourceDescription.Name, PayloadType.dynamicAudio, null, 0, 200, castEndPoint);
                    aCapture = new CaptureAudio(rtpSenderVoice);
                }
                try
                {
                    aCapture.Start();
                    mnuiSendVoice.Text = "Stop voice";
                }
                catch { }
            }
            else
            {
                try
                {
                    aCapture.Pause();
                    mnuiSendVoice.Text = "Send voice";
                }
                catch { }
            }
            btnStartVoice.Checked = mnuiSendVoice.Text.Equals( "Stop voice");
        }

        private void mnuiSendFile_Click(object sender, EventArgs e)
        {
            Application.DoEvents();
            string fileName = "";
            FileObject fo = null;
            if (dlgSendFile.ShowDialog() == DialogResult.OK)
            {
                fileName = dlgSendFile.FileName;
                if (!File.Exists(fileName))
                {
                    MessageBox.Show(string.Format("File: {0} not existed", fileName), "Read file error",  MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
                FileInfo fi = new FileInfo(fileName);
                int mbs = (int) fi.Length / (1024*1024);
                if (mbs > Global.Settings.MaxFileSize)
                {
                    MessageBox.Show(string.Format("The size of File: {0} is over maximum {1} MB", fileName, Global.Settings.MaxFileSize), "Read file error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
                fo = new FileObject(fileName);                
            }
            else
            {
                return;
            }
            using (MemoryStream ms = new MemoryStream())
            {
                if (rtpSenderFile == null)
                {
                    rtpSenderFile = Global.RtpSession.CreateRtpSenderFec(Global.SourceDescription.Name, PayloadType.FileTransfer, null, 0, 200, castEndPoint);
                    bf = new BinaryFormatter();
                    string testPath = Path.Combine(Application.StartupPath, "TestTransfer.test");
                    if (File.Exists(testPath))
                    {
                        FileObject testFo = new FileObject(testPath);
                        bf.Serialize(ms, testFo);
                        byte[] testData = new byte[ms.Position];
                        ms.Position = 0;
                        ms.Read(testData, 0, testData.Length);
                        rtpSenderFile.Send(testData);
                    }
                }
                ms.Position = 0;
                bf.Serialize(ms, fo);
                byte[] data = new byte[ms.Position];
                ms.Position = 0;
                ms.Read(data, 0, data.Length);
                rtpSenderFile.Send(data);
            }
        }

        private void mnuiLogout_Click(object sender, EventArgs e)
        {
            if (Global.SourceDescription.IsMainSource)
            {
                if (Global.ServiceStarted &&
                    MessageBox.Show("If you logout, the service on the server will stop immediatelly.\nAre you sure you want to logout ?",
                    "Warning",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question)
                            == DialogResult.No)
                {
                    return;
                }                 
            }
            if(Global.SourceDescription.IsMainSource)
                Global.Svc.StopCastService();
            Cleanup();
            FrmMain_Load(null, new EventArgs());
        }

        private void mnuiExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void aboutTheLectureToolToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FrmAbout frmAbout = new FrmAbout();
            frmAbout.ShowDialog(this);
        }
    }
}